﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using iNet.Media.Imaging.Exif;
using iNet.Media.Imaging.Jpeg;
using iNet.Media.Xmp;

namespace iNet.Media.Imaging
{
	/// <summary>
	/// JPEG 影像解碼工作。
	/// </summary>
	public class JpegImageDecodeTask : BasicImageDecodeTask
	{
		#region 建構子
		/// <summary>
		/// 初始化新的 JpegImageDecodeTask 執行個體。
		/// </summary>
		public JpegImageDecodeTask()
			: base(ImageFormats.Jpeg)
		{ }
		/// <summary>
		/// 初始化新的 JpegImageDecodeTask 執行個體。
		/// </summary>
		/// <param name="source">影像儲存來源。</param>
		public JpegImageDecodeTask(MediaStorage source)
			: base(ImageFormats.Jpeg, source, 0, 0)
		{ }
		#endregion
		#region DecodeImageOrientationOverride
		/// <summary>
		/// 解碼影像的方向。
		/// </summary>
		/// <param name="version">目前的工作版本。</param>
		/// <param name="stream">提供影像資料的資料流。</param>
		/// <returns>影像的方向。</returns>
		internal override ImageOrientation DecodeImageOrientationOverride(Tasks.TaskVersion version, Stream stream)
		{
			//讀取中繼資料
			byte[] buffer = new byte[2];
			bool readSegment = true;
			while (readSegment)
			{
				//讀取 segment 開頭
				if (stream.Read(buffer, 0, 2) < 2)
					break;
				if (buffer[0] != 0xFF)
					break;
				JpegSegmentType segmentType = (JpegSegmentType)buffer[1];

				//取得 segment 資料長度
				if (segmentType == JpegSegmentType.SOI)
					continue;
				if (segmentType == JpegSegmentType.SOS)
					break;
				if (stream.Read(buffer, 0, 2) < 2)
					break;
				int length = (buffer[0] << 8) | buffer[1];

				//讀取 segment 資料
				if (length < 2)
					break;
				if (length == 2)
					continue;
				length -= 2;
				byte[] data = new byte[length];
				switch (segmentType)
				{
					case JpegSegmentType.APP1:
						if (stream.Read(data, 0, length) < length)
							break;
						using (MemoryStream mstream = new MemoryStream(data))
						{
							//讀取資料
							if (BitConverter.LittleEndian.ToUInt32(data, 0) == 0x66697845 && BitConverter.LittleEndian.ToUInt16(data, 4) == 0)																								//EXIF
							{
								mstream.Position = 6;
								TiffReader tiffReader = new TiffReader(mstream);
								while (tiffReader.ReadNextTag())
								{
									if (tiffReader.Ifd == Ifd.Ifd0 && tiffReader.TagId == 274 && tiffReader.TagType == ExifTagType.UInt16)
									{
										ExifTag tag = tiffReader.Tag;
										if (tag != null && tag.Count == 1 && tag.Data != null && tag.Data.Length == 2)
											return (ImageOrientation)(int)tag.BitConverter.ToUInt16(tag.Data);
									}
								}
							}
						}
						break;
					default:
						if (stream.CanSeek)
							stream.Seek(length, SeekOrigin.Current);
						else if (stream.Read(data, 0, length) < length)
							readSegment = false;
						break;
				}
			}
			return ImageOrientation.Rotate0;
		}
		#endregion
		#region DecodeMetadataOverride
		internal override IMetadata DecodeMetadataOverride(Stream stream, bool isReadOnly)
		{
			//建立集合
			List<ExifTag> exifTags = new List<ExifTag>();
			List<XmpProperty> xmpProperties = new List<XmpProperty>();

			//讀取中繼資料
			byte[] buffer = new byte[2];
			bool readSegment = true;
			while (readSegment)
			{
				//讀取 segment 開頭
				if (stream.Read(buffer, 0, 2) < 2)
					break;
				if (buffer[0] != 0xFF)
					break;
				JpegSegmentType segmentType = (JpegSegmentType)buffer[1];

				//取得 segment 資料長度
				if (segmentType == JpegSegmentType.SOI)
					continue;
				if (segmentType == JpegSegmentType.SOS)
					break;
				if (stream.Read(buffer, 0, 2) < 2)
					break;
				int length = (buffer[0] << 8) | buffer[1];

				//讀取 segment 資料
				if (length < 2)
					break;
				if (length == 2)
					continue;
				length -= 2;
				byte[] data = new byte[length];
				switch (segmentType)
				{
					case JpegSegmentType.APP1:
						{
							if (stream.Read(data, 0, length) < length)
								break;
							using (MemoryStream mstream = new MemoryStream(data))
							{
								//讀取資料
								if (BitConverter.LittleEndian.ToUInt32(data, 0) != 0x66697845 || BitConverter.LittleEndian.ToUInt16(data, 4) != 0)		//XMP
								{
									//確認是否為 XMP
									mstream.Position = 0;
									if (!this.IsXmpHeader(mstream))
										break;

									//讀取 XMP Property
									using (XmpPropertyEnumerator enumerator = new XmpPropertyEnumerator(mstream))
									{
										while (enumerator.MoveNext())
											xmpProperties.Add(enumerator.Current);
									}
								}
								else																								//EXIF
								{
									mstream.Position = 6;
									exifTags.AddRange((new TiffReader(mstream)).ReadAllTags());
								}
							}
						}
						break;
					default:
						if (stream.CanSeek)
							stream.Seek(length, SeekOrigin.Current);
						else if (stream.Read(data, 0, length) < length)
							readSegment = false;
						break;
				}
			}

			//回傳
			return new TiffMetadata(exifTags, xmpProperties, isReadOnly);
		}
		#endregion
		#region DecodePixelSize
		internal override Size<int> DecodePixelSize(Stream stream)
		{
			//取得影像大小
			byte[] buffer = new byte[2];
			bool readSegment = true;
			while (readSegment)
			{
				//讀取 segment 開頭
				if (stream.Read(buffer, 0, 2) < 2)
					break;
				if (buffer[0] != 0xFF)
					break;
				JpegSegmentType segmentType = (JpegSegmentType)buffer[1];

				//取得 segment 資料長度
				if (segmentType == JpegSegmentType.SOI)
					continue;
				if (segmentType == JpegSegmentType.SOS)
					break;
				if (stream.Read(buffer, 0, 2) < 2)
					break;
				int length = (buffer[0] << 8) | buffer[1];

				//讀取 segment 資料
				if (length < 2)
					break;
				if (length == 2)
					continue;
				length -= 2;
				byte[] data = new byte[length];
				switch (segmentType)
				{
					case JpegSegmentType.SOF0:
					case JpegSegmentType.SOF2:
						{
							//讀取資料
							if (stream.Read(data, 0, length) < length)
							{
								readSegment = false;
								break;
							}

							//取得影像大小
							return new Size<int>((data[3] << 8) | data[4], (data[1] << 8) | data[2]);
						}
					default:
						if (stream.CanSeek)
							stream.Seek(length, SeekOrigin.Current);
						else if (stream.Read(data, 0, length) < length)
							readSegment = false;
						break;
				}
			}

			//擲出例外
			throw new MediaDecodeFailedException(MediaDecodeFailedReason.InvalidFormat);
		}
		#endregion
		#region IsXmpHeader
		bool IsXmpHeader(Stream stream)
		{
			byte[] buffer = new byte[29];
			if (stream.Read(buffer, 0, 29) < 29)
				return false;
			return (Encoding.ASCII.GetString(buffer, 0, 28) == "http://ns.adobe.com/xap/1.0/");
		}
		#endregion
	}
}
